﻿// <copyright file="MinitabBroker.cs" company="The University of Scranton"> 
// Copyright (c) 2010 All Rights Reserved, http://sdpmt.codeplex.com 
// 
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY  
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE. 
// 
// </copyright> 
// <author>Luke Benedict</author> 
// <email>luke.benedict@ieee.org</email> 
// <date>2010-11-04</date> 
// <summary>Contains a static class that facilitates communication with Minitab</summary>

namespace Sdpmt.Minitabinterface
{

    #region Namespace Usage
    
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Windows.Forms;
    using System.Windows.Media.Imaging;
    using Sdpmt.Minitabinterface;

    #endregion
    
    /// <summary>
    /// This class provides an interface to the Minitab 16 Type Library
    /// </summary>
    /// <remarks>The Minitab 16.0 Type Library allows programmers to access the COM components of the Minitab statistical software.</remarks>
    public class MinitabBroker
    {
        #region Private Members

        /// <summary>
        /// A reference to an instance of the Minitab application
        /// </summary>
        /// <remarks>The Application object serves as the root node in the Minitab automation server object hierarchy. 
        /// The Application object is the only object in the hierarchy that can be created by the client. 
        /// All the lower objects in the hierarchy are accessed through the Application object or through objects contained 
        /// within the Application object. The Application object provides an interface to allow the client to set and get application-wide global properties.</remarks>
        private Mtb.IApplication m_MinitabApplication;

        /// <summary>
        /// A reference to a Minitab project within a Minitab application instance
        /// </summary>
        /// <remarks>The Project object contains all the information related to an individual project, including the Worksheets collection and the Commands collection.</remarks>
        private Mtb.IProject m_MinitabProject;

        #endregion

        #region Private Methods

        /// <summary>
        /// Parses a Minitab table of dates and numbers into a .NET IDictionary of DateTime, double
        /// </summary>
        /// <param name="table">String of data that is the Minitab table</param>
        /// <param name="firstDate">First date of the data appearing in the Minitab table</param>
        /// <returns>The .NET IDictionary which is a list of DateTime and double</returns>
        private IDictionary<DateTime, double> ParseForecast(string table, DateTime firstDate)
        {
            string stringToParse;
            string[] tokens = new string[1];
            double defectCount;     
            string[] predictionToken = new string[2];
            DateTime curDate;
            IDictionary<DateTime, double> forecast;
            
            curDate = firstDate;       
            stringToParse = table.TrimStart(null);            

            forecast = new SortedDictionary<DateTime, double>();

            
            tokens = stringToParse.Split(new string[]{"\r\n"},StringSplitOptions.RemoveEmptyEntries);
            

            //If there were any tokens found, interate over them
            foreach (string str in tokens)
            {
                if(Char.IsNumber(str[0]))
                {
                    predictionToken = str.Split(new string[] { " " },StringSplitOptions.RemoveEmptyEntries);
                    defectCount = double.Parse(predictionToken[1]);
                    forecast.Add(curDate, Math.Round(defectCount));
                    curDate = curDate.AddDays(1);
                }            
            }//end foreach

            return forecast;

        }//end ParseForecast

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns a Trend with a specified Forecast period
        /// </summary>
        /// <param name="data">IDictionary of DateTime,double defect metrics</param>
        /// <param name="forecastPeriod">Period to forecast forward</param>
        /// <param name="trendType">Model that the data tends to fit</param>
        /// <returns>Path to the image of the graph</returns>
        public Tuple<Image,IDictionary<DateTime,double>> GetForecast(IDictionary<DateTime, double> data, int forecastPeriod, MinitabConstants.TrendModelType trendType)
        {
            Mtb.IColumns mtbColumns;
            Mtb.IColumn mtbColumn;
            Mtb.ITable mtbTable;
            Mtb.IGraph mtbGraph;
            IDictionary<DateTime,double> forecast;
            Image graphImage;

            //set up a new dictionary to hold the tabular forecast data that is expected to be returned by Minitab
            forecast = new SortedDictionary<DateTime,double>();
            
            try
            {
                //Instantiate an instance of the Minitab application
                m_MinitabApplication = new Mtb.Application();
            }
            catch (System.Runtime.InteropServices.COMException)
            {
                //Every time COMException is being thrown. Don't know why.
                //Trying to instantiate the application a 2nd time works
                if (m_MinitabApplication == null)
                {
                    m_MinitabApplication = new Mtb.Application();
                }
            }

            //Set preferences for the Minitab application
            m_MinitabApplication.UserInterface.Visible = false;

            //Get the active project
            m_MinitabProject = m_MinitabApplication.ActiveProject;

            //Get a reference to the collection of columns in the active worksheet
            mtbColumns = m_MinitabApplication.ActiveProject.ActiveWorksheet.Columns;

            //Add one column called Month to the active worksheet's columns
            mtbColumn = mtbColumns.Add(Type.Missing, Type.Missing, 1);
            mtbColumn.Name = "Month";
            object pp = data.Keys.ToArray();
            mtbColumn.SetData(ref pp, 1, data.Keys.Count);

            //Add one column called Defects to the active worksheet's columns
            mtbColumn = mtbColumns.Add(Type.Missing, Type.Missing, 1);
            mtbColumn.Name = "Defects";
            object qq = data.Values.ToArray();
            mtbColumn.SetData(ref qq, 1, data.Values.Count);

            string strTrendType = string.Empty;

            //Convert the supplied trend type to a string that
            //can be used for issuing the command to Minitab
            switch (trendType)
            {
                case MinitabConstants.TrendModelType.GROWTH:
                    strTrendType = "GROWTH;";
                    break;
                case MinitabConstants.TrendModelType.QUADRATIC:
                    strTrendType = "QUADRATIC;";
                    break;
                case MinitabConstants.TrendModelType.SCURVE:
                    strTrendType = "SCURVE;";
                    break;
                case MinitabConstants.TrendModelType.LINEAR:
                    strTrendType = string.Empty;
                    break;
                default:
                    strTrendType = string.Empty;
                    break;
            
            }// end switch
            
            // Issue the command to Minitab to produce a Trend graph with a Forecast
            m_MinitabProject.ExecuteCommand("TREND C2; " + strTrendType + " FORECASTS " + forecastPeriod.ToString() + ".");
           // m_MinitabProject.ExecuteCommand("PPLOT C2; WEIBULL.");
            // m_MinitabProject.ExecuteCommand("NLINEAR C2");

            //Loop through from commands
            foreach (Mtb.IMTBCommand command in m_MinitabProject.Commands)
            {             

                //Loop through outputs of each command
                foreach (Mtb.IOutput mtbOutObj in command.Outputs)
                {                   

                    //switch on the type of the output
                    switch (mtbOutObj.OutputType)
                    {
                        case Mtb.MtbOutputTypes.OTGraph: //graph
                            mtbGraph = mtbOutObj.Graph;
                            mtbGraph.CopyToClipboard();
                            break;
                        case Mtb.MtbOutputTypes.OTTitle:
                            break;
                        case Mtb.MtbOutputTypes.OTTable:
                            mtbTable = mtbOutObj.Table;
                            //Check to see if this table starts with the word "Forecasts"
                            //so to be sure that this is the table that we want and not some other table
                            if(mtbTable.Text.TrimStart(new char[]{'\r','\n'}).StartsWith("Forecasts"))
                            { 
                                forecast  = ParseForecast(mtbTable.Text,data.Keys.Last().AddDays(1));
                            }
                            break;
                        default:
                            break;
                    
                    }// end switch
                
                }// end inner foreach
            
            }// end outer foreach

            // Delete the active project and kill Minitab
            m_MinitabApplication.ActiveProject.Delete();
            m_MinitabApplication.Quit();
            
            graphImage = Clipboard.GetImage();            

            // Return the path to the image
            return Tuple.Create(graphImage,forecast);
       
        }// end GetForecast
        
        /// <summary>
        /// Gets an image of a Fitted Line Plot from Minitab
        /// </summary>
        /// <param name="data">SortedDictionary of DateTime/double</param>
        /// <returns>BitmapSource, an image</returns>
        public Image GetFittedLinePlot(IDictionary<DateTime,double> data)
        {
            Mtb.IColumns mtbColumns;
            Mtb.IColumn mtbColumn;
            Mtb.IMTBCommand m_MinitabCommand;
            Mtb.IOutput mtbOutObj;
            Mtb.IGraph mtbGraph;
            Image bmp;
            object defectsArray;
            object datesArray;

            try
            {
                //Instantiate an instance of the Minitab application
                m_MinitabApplication = new Mtb.Application();
            }
            catch (System.Runtime.InteropServices.COMException)
            {
                //Every time COMException is being thrown. Don't know why.
                //Trying to instantiate the application a 2nd time works
                if (m_MinitabApplication == null)
                {
                    m_MinitabApplication = new Mtb.Application();
                }
            }

            //Set preferences for the Minitab application
            m_MinitabApplication.UserInterface.Visible = false;

            //Get the active project
            m_MinitabProject = m_MinitabApplication.ActiveProject;

            //Get a reference to the collection of columns in the active worksheet
            mtbColumns = m_MinitabApplication.ActiveProject.ActiveWorksheet.Columns;


            int[] b = new int[data.Keys.Count];
            

            for (int i = 1; i < data.Keys.Count+1; i++)
            {
                b[i - 1] = i;
            }

            datesArray = b;

            
            //Add one column called Month to the active worksheet's columns
            mtbColumn = mtbColumns.Add(Type.Missing, Type.Missing, 1);
            mtbColumn.Name = "Elapsed Days";
            mtbColumn.SetData(ref datesArray, 1, data.Keys.Count);

            //Add one column called Defects to the active worksheet's columns
            mtbColumn = mtbColumns.Add(Type.Missing, Type.Missing, 1);
            mtbColumn.Name = "Defects";
            defectsArray = data.Values.ToArray();
            mtbColumn.SetData(ref defectsArray, 1, data.Values.Count);

            //Command the project to generate a scatter plot of C2 to C1
            m_MinitabProject.ExecuteCommand("FITLINE C2 C1; POLY 3.");

            //Loop through from commands
            for (int i = 1; i < m_MinitabProject.Commands.Count + 1; i++)
            {
                m_MinitabCommand = m_MinitabProject.Commands.Item(i);

                //Loop through outputs of each command
                for (int j = 1; j < m_MinitabCommand.Outputs.Count + 1; j++)
                {
                    mtbOutObj = m_MinitabCommand.Outputs.Item(j);                    

                    //switch on the type of the output
                    switch (mtbOutObj.OutputType)
                    {
                        case Mtb.MtbOutputTypes.OTGraph: //graph
                            mtbGraph = mtbOutObj.Graph;
                            mtbGraph.CopyToClipboard();
                            break;
                        case Mtb.MtbOutputTypes.OTFormula:                        
                            break;
                        case Mtb.MtbOutputTypes.OTTable:                            
                            break;
                        case Mtb.MtbOutputTypes.OTMessage:
                            break;
                        default:
                            break;
                    }
                }
            }

            //Delete the active project and kill Minitab
            m_MinitabApplication.ActiveProject.Delete();
            m_MinitabApplication.Quit();

            bmp = Clipboard.GetImage();       
            
            //Return the path to the image
            return bmp;
        
        }//end GetFittedLinePlot  

    }//end class

        #endregion

}//end namespace
